<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <style>
    html, body {
      padding: 0;
      margin: 0;
      height: 100%;
      overflow: hidden;
    }

    #cas {
      display: block;
      margin: 0 auto;
      border: 1px solid;
      height: 100%;
      max-height: 800px;
    }
  </style>
  <title>接水管游戏</title>
</head>
<body>
<canvas id="cas" width="800" height="800">您的浏览器不支持canvas</canvas>
<div style="display:none">
  <img src="start.png" alt="" id="start">
  <img src="pipes0.jpg" alt="" id="pipe0">
  <img src="pipe1.png" alt="" id="pipe1">
  <img src="pipe2.png" alt="" id="pipe2">
</div>
<script src="data.js"></script>
<script>
  window.onload = (function() {
    var canvas = document.getElementById("cas");
    var ctx = canvas.getContext('2d');
    var cols = 8,	//拼图列
        rows = 4,	//拼图行
        jiange = 20,  //拼图区与canvas之间的边缘宽度
        boxWidth = (canvas.width - 2 * jiange) / cols,	//水管块宽高
        marginTop = (canvas.height - boxWidth * rows) / 2,    //拼图区与顶部高度
        pipeWidth = 30,	//水管宽度
        canclick = true,	//当前是否可以点击
        activityBox = null, //用于保存运动中的水管块
        boxes = [],    //保存水管块的容器
        toolBoxes = [],     //保存工具对象
        connectSuccess = false;
    ctx.lineWidth = 2;

    return function() {
      var Box = function(center, style, Gateway, angle, coordinate) {
        this.center = center;
        this.style = style;
        this.angle = angle;
        this.endangle = angle;
        this.Gateway = Gateway;
        this.coordinate = coordinate;
      };
      Box.prototype = {
        constructor: Box,
        draw: function() {
          this.setHole();

          if (this.angle !== this.endangle) {
            canclick = false;
            this.rotate();
          }

          var ext = [
            { x: 0, y: -boxWidth / 2 },
            { x: boxWidth / 2, y: 0 },
            { x: 0, y: boxWidth / 2 },
            { x: -boxWidth / 2, y: 0 },
          ]
          ctx.save();
          ctx.translate(this.center.x, this.center.y);
          ctx.rotate(this.angle);
          //画管道
          switch (this.style) {
            case 0:
              break;
            case 1:
              ctx.drawImage(document.getElementById("pipe1"), -boxWidth / 2, -boxWidth / 2, boxWidth, boxWidth);
              break;
            case 2:
              ctx.drawImage(document.getElementById("pipe2"), -boxWidth / 2, -boxWidth / 2, boxWidth, boxWidth);
              break;
            case 3:
              ctx.drawImage(document.getElementById("start"), -boxWidth / 2, -boxWidth / 2, boxWidth, boxWidth);
              break;
          }
          if (this.style && this.hovered) {
            ctx.strokeRect(-boxWidth / 2, -boxWidth / 2, boxWidth, boxWidth);
          }
          ctx.restore();
        },
        rotate: function() {
          if (Math.abs(this.endangle - this.angle) <= 0.01) {
            canclick = true;
            this.endangle = this.endangle >= 2 * Math.PI ? 0 : this.endangle;
            this.angle = this.endangle;

            if (this.style === 3) {
              var result = connectPipes();
              if (result) {
                // alert('成功连通')
                connectSuccess = true;
              }
              else {
                alert("游戏失败")
                window.location.reload();
              }
            }
          }
          else {
            this.angle += (this.endangle - this.angle) * 0.2;
          }
        },
        setHole: function() {
          if (this.style === 1) {
            var zl = this.endangle / (0.5 * Math.PI);
            var initHole1 = 0, initHole2 = 2;
            this.inHole = (initHole1 + zl) >= 4 ? ((initHole1 + zl) - 4) : (initHole1 + zl);
            this.outHole = (initHole2 + zl) >= 4 ? ((initHole2 + zl) - 4) : (initHole2 + zl);
          }
          else if (this.style === 2) {
            var zl = this.endangle / (0.5 * Math.PI);
            var initHole1 = 1, initHole2 = 2;
            this.inHole = (initHole1 + zl) >= 4 ? ((initHole1 + zl) - 4) : (initHole1 + zl);
            this.outHole = (initHole2 + zl) >= 4 ? ((initHole2 + zl) - 4) : (initHole2 + zl);
          }
        }
      }

      //判断水管连通性
      function connectPipes() {
        var index = 0;
        while (1) {
          var result = getHole(boxes[index]);
          if (boxes[index + result.nextBox]) {
            if (result.hole === boxes[index + result.nextBox].inHole) {
              index = index + result.nextBox;
            }
            else if (result.hole === boxes[index + result.nextBox].outHole) {
              var num = boxes[index + result.nextBox].inHole;
              boxes[index + result.nextBox].inHole = result.hole;
              boxes[index + result.nextBox].outHole = num;
              index = index + result.nextBox;
            }
            else {
              break;
            }
          }
          else {
            break;
          }
        }
        if (index === boxes.length - 1) return true;
        else return false;
      }

      function getHole(box) {
        var hole = "0";
        var nextBox = 0;
        switch (box.outHole) {
          case 0 :
            hole = 2;
            nextBox = -cols;
            break;
          case 1 :
            hole = 3;
            if (box.coordinate.cols === cols - 1) {
              nextBox = 1000000;
            }
            else nextBox = 1;
            break;
          case 2 :
            hole = 0;
            nextBox = cols;
            break;
          case 3 :
            hole = 1;
            if (box.coordinate.cols === 0) {
              nextBox = 1000000;
            }
            else nextBox = -1;
            break;
        }
        return { hole: hole, nextBox: nextBox };
      }

      //数组扩展循环遍历方法
      Array.prototype.foreach = function(callback) {
        for (var i = 0; i < this.length; i++) {
          callback.apply(this[i], [i]);
        }
      };

      canvas.addEventListener("mousemove", function() {
        var bili = canvas.offsetHeight / canvas.height;
        var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetLeft;
        var y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetTop;
        boxes.foreach(function() {
          var x1 = this.center.x - boxWidth / 2;
          var x2 = this.center.x + boxWidth / 2;
          var y1 = this.center.y - boxWidth / 2;
          var y2 = this.center.y + boxWidth / 2;
          this.hovered = (x >= x1 * bili && x <= x2 * bili && y >= y1 * bili && y <= y2 * bili && !this.Gateway);
        });
      });

      window.addEventListener("click", function() {
        if (!canclick) return false;

        var bili = canvas.offsetHeight / canvas.height;

        canclick = false;

        var x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft - canvas.offsetLeft;
        var y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop - canvas.offsetTop;

        var isAnimate = false;
        boxes.foreach(function() {
          var x1 = this.center.x - boxWidth / 2;
          var x2 = this.center.x + boxWidth / 2;
          var y1 = this.center.y - boxWidth / 2;
          var y2 = this.center.y + boxWidth / 2;

          if (x >= x1 * bili && x <= x2 * bili && y >= y1 * bili && y <= y2 * bili && !this.Gateway) {
            this.endangle = this.endangle + 0.5 * Math.PI;
            activityBox = this;
            isAnimate = true;
          }
        });

        toolBoxes.foreach(function() {
          var x1 = this.center.x - boxWidth / 2;
          var x2 = this.center.x + boxWidth / 2;
          var y1 = this.center.y - boxWidth / 2;
          var y2 = this.center.y + boxWidth / 2;

          if (this.constructor === Box && x >= x1 * bili && x <= x2 * bili && y >= y1 * bili && y <= y2 * bili && this.style === 3) {
            this.endangle = this.endangle + 4 * Math.PI;
            isAnimate = true;
          }
        });

        if (!isAnimate) {
          canclick = true;
        }
      }, false);

      //获得随机数，如果c不为空则为获取除c之外的随机数
      function getRandom(a, b, c) {
        if (c !== null) {
          var random = 0;
          while (1) {
            var num = Math.round(Math.random() * (b - a) + a)
            if (num !== c) {
              random = num;
              break;
            }
          }
          return random;
        }
        else {
          return Math.round(Math.random() * (b - a) + a);
        }
      }

      var Painter = {
        drawHaoZe: function() {    //绘制饮水器
          ctx.save();
          ctx.fillStyle = "rgba(0,0,0,0.8)";
          ctx.fillRect(315, 105 + marginTop, 172, 172);
          ctx.strokeRect(315, 105 + marginTop, 172, 172);
          ctx.fillStyle = "#FFF";
          ctx.font = "20px 微软雅黑";
          ctx.textAlign = "center";
          ctx.textBaseLine = "middle";
          ctx.fillText("净水器", canvas.width / 2, canvas.height / 2);
          ctx.restore();
        },
        drawBackGround: function() {
          ctx.save();
          ctx.fillStyle = "#BB8350";
          ctx.strokeRect(0, boxes[0].center.y - boxWidth / 2 - jiange, boxWidth * cols + 2 * jiange, boxWidth * rows + 2 * jiange);

          for (var i = 0; i < rows; i++) {
            for (var j = 0; j < cols; j++) {
              ctx.drawImage(document.getElementById("pipe0"), (j * boxWidth) + jiange, (i * boxWidth) + marginTop, boxWidth, boxWidth);
            }
          }

          ctx.restore();
        },
        drawWaterBox: function() {
          var wb1 = new WaterBox({ x: canvas.width / 2, y: jiange + 50 }, false, canvas.width - (2 * jiange), 100)
          var wb2 = new WaterBox({
            x: canvas.width / 2,
            y: canvas.height - jiange - 50
          }, true, canvas.width - (2 * jiange), 100)
          toolBoxes.push(wb1);
          toolBoxes.push(wb2);
        },
        drawOutPipe: function() {
          var t1 = new Box({ x: boxes[0].center.x, y: boxes[0].center.y - boxWidth }, 1, true, 0);
          var t2 = new Box({ x: boxes[0].center.x, y: boxes[0].center.y - boxWidth }, 3, false, 0);
          var t3 = new Box({
            x: boxes[boxes.length - 1].center.x,
            y: boxes[boxes.length - 1].center.y + boxWidth
          }, 1, true, 0);
          toolBoxes.push(t1);
          toolBoxes.push(t2);
          toolBoxes.push(t3);
        }
      }

      var WaterBox = function(center, isgood, width, height) {
        this.center = center;
        this.isgood = isgood;
        this.w = width;
        this.h = height;
        this.waterHeight = isgood ? 0 : this.h * 2 / 3;
        this.start = false;
        this.end = false;
      }
      WaterBox.prototype = {
        constructor: WaterBox,
        draw: function() {
          ctx.save();
          ctx.strokeStyle = "#000";
          ctx.strokeRect(this.center.x - this.w / 2, this.center.y - this.h / 2, this.w, this.h);
          ctx.fillStyle = this.isgood ? "rgba(52,160,209,1)" : "rgba(164,164,164,1)";

          if (this.start) {
            this.isgood ? this.addWater() : this.reduceWater();
            if (this.isgood) {
              var h = this.waterHeight === this.h * 2 / 3 ? 0 : this.h
              ctx.fillRect(this.center.x + (this.w / 2 - 66), this.center.y - this.h / 2, 37, h)
              if (h === 0) {
                this.end = true
              }
            }
          }

          ctx.fillRect(this.center.x - this.w / 2, (this.center.y - this.h / 2) + (this.h - this.waterHeight), this.w, this.waterHeight);
          ctx.restore();
        },
        reduceWater: function() {
          this.waterHeight = this.waterHeight <= 0 ? 0 : this.waterHeight - 0.5;
        },
        addWater: function() {
          this.waterHeight = this.waterHeight >= this.h * 2 / 3 ? this.h * 2 / 3 : this.waterHeight + 0.5;
        }
      }

      //动画初始化
      function initAnimation() {
        for (var i = 0; i < rows; i++) {
          for (var j = 0; j < cols; j++) {
            var b;
            //低难度
            // var b = new Box({x:(j*boxWidth)+boxWidth/2+jiange , y:(i*boxWidth)+boxWidth/2+marginTop} , 0 , true , 0 , {rows:i , cols:j});

            //高难度
            if (i > 0 && i < 3 && j > 2 && j < 5) {
              b = new Box({
                x: (j * boxWidth) + boxWidth / 2 + jiange,
                y: (i * boxWidth) + boxWidth / 2 + marginTop
              }, 1, true, 0.5 * Math.PI, { rows: i, cols: j });
            } else {
              b = new Box({
                x: (j * boxWidth) + boxWidth / 2 + jiange,
                y: (i * boxWidth) + boxWidth / 2 + marginTop
              }, getRandom(0, 2), false, parseInt(getRandom(0, 3)) * 0.5 * Math.PI, { rows: i, cols: j });
            }
            boxes.push(b);
          }
        }

        var n = getRandom(0, allPath.length - 1);
        var path = allPath[n];
        path.foreach(function() {
          var index = this.rows * cols + this.cols;
          if ((this.rows == 0 && this.cols == 0) || (this.rows == (rows - 1) && this.cols == (cols - 1))) {
            boxes[index] = new Box({
              x: (this.cols * boxWidth) + boxWidth / 2 + jiange,
              y: (this.rows * boxWidth) + boxWidth / 2 + marginTop
            }, this.style, true, 0, { rows: this.rows, cols: this.cols });
          }
          else if (this.rows > 0 && this.rows < 3 && this.cols > 2 && this.cols < 5) {
            boxes[index] = new Box({
              x: (this.cols * boxWidth) + boxWidth / 2 + jiange,
              y: (this.rows * boxWidth) + boxWidth / 2 + marginTop
            }, this.style, true, 0.5 * Math.PI, { rows: this.rows, cols: this.cols });
          }
          else {
            boxes[index] = new Box({
              x: (this.cols * boxWidth) + boxWidth / 2 + jiange,
              y: (this.rows * boxWidth) + boxWidth / 2 + marginTop
            }, this.style, false, parseInt(getRandom(0, 3)) * 0.5 * Math.PI, { rows: this.rows, cols: this.cols });
          }
        });

        Painter.drawWaterBox();
        Painter.drawOutPipe();

        animate();
      }

      //动画每一帧的发生舞台
      function animate() {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        Painter.drawBackGround();

        //如果是当前运动中的水管块则跳过，当其他水管块绘制完后再单独绘制运动中的水管块，以保证其不被其他拼图覆盖
        var number;
        boxes.foreach(function(index) {
          if (this == activityBox) {
            number = index;
          }
          else this.draw();
        });
        if (activityBox) boxes[number].draw();

        toolBoxes.foreach(function() {
          if (this.constructor === WaterBox && connectSuccess) {
            this.start = true;
            if (this.end) {
              alert("你赢啦")
              connectSuccess = false;
              window.location.reload();
            }
          }
          this.draw();
        })
        Painter.drawHaoZe();
        RAF(animate)
      }

      //动画兼容性写法
      window.RAF = (function() {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) {
              window.setTimeout(callback, 1000 / 60);
            };
      })();

      initAnimation();
    }
  })();
</script>
</body>
</html>